package com.g1.screens.services;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;

import org.newdawn.slick.GameContainer;
import org.newdawn.slick.Graphics;
import org.newdawn.slick.Image;
import org.newdawn.slick.SlickException;
import org.newdawn.slick.geom.Vector2f;
import org.newdawn.slick.state.StateBasedGame;

import com.g1.screens.enumerations.Animation;
import com.g1.screens.enumerations.BlockType;
import com.g1.screens.model.Block;
import com.g1.screens.model.PosInBlocks;

public class BlockService {
	
	public final static int ROWS = 6;
	public final static int COLUMNS = 8;
	public final static Vector2f position = new Vector2f(355, 45);
	
	private Image blocksResource;
	private Block[][] blocks;
	private Block[] selectedBlock;
	
	private Vector2f[] selectedBlocksHistoricalPos;
	
	private List<Animation> animation2Play;
	
	private Random r;
	
	public BlockService() throws SlickException {
		r = new Random();
		selectedBlocksHistoricalPos = new Vector2f[2];
		animation2Play = new ArrayList<Animation>();
		blocksResource = new Image("data/blocks.png");
		blocks = new Block[COLUMNS][ROWS];
		for(int i = 0; i<ROWS; i++){
			for(int j = 0; j<COLUMNS; j++){
				blocks[j][i] = generateRandomBlock(j,i, blocksResource);
			}
		}
		selectedBlock = new Block[2];
	}
	
	public void clearSelectedBlock(){
		for(Block b : selectedBlock){
			if(b!=null)
			b.setSelected(false);
		}
		selectedBlock = new Block[2];
	}
	
	public void clearAnimationBuffer(){
		animation2Play = new ArrayList<Animation>();
	}
	
	public String getAnimationList(){
		StringBuffer sb = new StringBuffer();
		for(Animation a : animation2Play){
			sb.append(a.name()+" ");
		}
		return sb.toString();
	}
	
	public boolean hasAnimation(){
		return animation2Play!=null && animation2Play.size()>0;
	}
		
	public Animation getNextAnimation2Play(){
		if(hasAnimation()) {
			return animation2Play.remove(0);
		} else {
			return Animation.IDLE;
		}
	}
	
	public Animation getCurrentAnimation2Play(){
		if(hasAnimation()) {
			return animation2Play.get(0);
		} else {
			return Animation.IDLE;
		}
	}
	
	public Block getFirstSelectedBlock(){
		return selectedBlock[0];
	}
	
	public Block getSecondSelectedBlock(){
		return selectedBlock[1];
	}
	
	public void setFirstSelectedBlock(final Block b){
		selectedBlocksHistoricalPos[0]=new Vector2f(b.getX(),b.getY());
		b.setSelected(true);
		selectedBlock[0] = b;
	}
	
	public void setSecondSelectedBlock(final Block b){
		selectedBlocksHistoricalPos[1]=new Vector2f(b.getX(),b.getY());
		b.setSelected(true);
		selectedBlock[1] = b;
	}
	
	public void update(GameContainer container, StateBasedGame game, int delta)
		throws SlickException {
		
	}
	
	public void render(GameContainer container, StateBasedGame game, Graphics g)
		throws SlickException {
		for(int i = 0; i<blocks.length;i++){
			for(int j = 0; j< blocks[0].length; j++){
				blocks[i][j].render(container, game, g);
			}
		}
	} 
	
	public Block[][] getBlocks(){
		return blocks;
	}
	
	public boolean calculateMove(Block source, Block destination){
		boolean result = false;
		
		if(source.isMovable() && destination.isMovable()){
			int rowS = source.getRow();
			int columnS = source.getColumn();
			int rowD = destination.getRow();
			int columnD = destination.getColumn();
			// change src / dest
			source.setRow(rowD);
			source.setColumn(columnD);
			destination.setRow(rowS);
			destination.setColumn(columnS);
			blocks[columnS][rowS] = destination;
			blocks[columnD][rowD] = source;
			// we try to switch blocks
			animation2Play.clear();
			animation2Play.add(Animation.SWITCH);
			result = calculate(blocks, rowS, columnS) || calculate(blocks, rowD, columnD);
			if(result) {
				animation2Play.add(Animation.GOODMOVE);
			} else {
				animation2Play.add(Animation.SWITCHBACK);
			}
			animation2Play.add(Animation.FALL);
			// undo (we use the same array)
			source.setRow(rowS);
			source.setColumn(columnS);
			destination.setRow(rowD);
			destination.setColumn(columnD);
			blocks[columnS][rowS] = source;
			blocks[columnD][rowD] = destination;
		}  
		return result;
	}
	
	public void switchAction(Block source, Block destination){
		
	}
	
	public void switchBackAction(Block source, Block destination){
		
	}
	
	/**
	 * the array is the array already switched, but not yet calculated
	 * @param underProcessBlocks
	 * @return
	 */
	private boolean calculate(Block[][] underProcessBlocks, final int row, final int column ){
		boolean isSuccess = false;
		int maxRowIndex = underProcessBlocks[0].length-1;
		int maxColumnIndex = underProcessBlocks.length-1;
		// test on column
		if(row-2>=0){
			// [ ] 1 case check ok 
			// [ ]
			// [.]
			isSuccess = underProcessBlocks[column][row].getType().getId() == underProcessBlocks[column][row-1].getType().getId() 
					&& underProcessBlocks[column][row].getType().getId()==underProcessBlocks[column][row-2].getType().getId();
			if(isSuccess) {
				underProcessBlocks[column][row].setToRemove(true);
				underProcessBlocks[column][row-1].setToRemove(true);
				underProcessBlocks[column][row-2].setToRemove(true);
				return true;
			}
		}
		if(row-1>=0 && row+1<=maxRowIndex){
			// [ ] 2 case check ok
			// [.]
			// [ ]
			isSuccess = underProcessBlocks[column][row].getType().getId() == underProcessBlocks[column][row-1].getType().getId() 
					&& underProcessBlocks[column][row].getType().getId()==underProcessBlocks[column][row+1].getType().getId();
			if(isSuccess) {
				underProcessBlocks[column][row].setToRemove(true);
				underProcessBlocks[column][row-1].setToRemove(true);
				underProcessBlocks[column][row+1].setToRemove(true);
				return true;
			}
		}
		if(row+2<=maxRowIndex){
			// [.] 3 case check ok
			// [ ]
			// [ ]
			isSuccess = underProcessBlocks[column][row].getType().getId() == underProcessBlocks[column][row+1].getType().getId() 
					&& underProcessBlocks[column][row].getType().getId()==underProcessBlocks[column][row+2].getType().getId();
			if(isSuccess)  {
				underProcessBlocks[column][row].setToRemove(true);
				underProcessBlocks[column][row+1].setToRemove(true);
				underProcessBlocks[column][row+2].setToRemove(true);
				return true;
			}
		}
		// test on row
		if (column-2>=0){
			// [ ][ ][.] 1 case is ok
			isSuccess = underProcessBlocks[column][row].getType().getId() == underProcessBlocks[column-2][row].getType().getId() 
					&& underProcessBlocks[column][row].getType().getId()==underProcessBlocks[column-1][row].getType().getId();
			if(isSuccess) {
				underProcessBlocks[column][row].setToRemove(true);
				underProcessBlocks[column-2][row].setToRemove(true);
				underProcessBlocks[column-1][row].setToRemove(true);
				return true;
			}
		}
		if(column-1>=0 && column+1<=maxColumnIndex){
			// [ ][.][ ] 2 case ok
			isSuccess = underProcessBlocks[column][row].getType().getId() == underProcessBlocks[column-1][row].getType().getId() 
					&& underProcessBlocks[column][row].getType().getId()==underProcessBlocks[column+1][row].getType().getId();
			if(isSuccess) { 
				underProcessBlocks[column][row].setToRemove(true);
				underProcessBlocks[column-1][row].setToRemove(true);
				underProcessBlocks[column+1][row].setToRemove(true);
				return true;
				}
			
		}
		if(column+2<=maxColumnIndex){
			// [.][ ][ ] 3 case ok
			isSuccess = underProcessBlocks[column][row].getType().getId() == underProcessBlocks[column+1][row].getType().getId() 
					&& underProcessBlocks[column][row].getType().getId()==underProcessBlocks[column+2][row].getType().getId();
			if(isSuccess) {
				underProcessBlocks[column][row].setToRemove(true);
				underProcessBlocks[column+1][row].setToRemove(true);
				underProcessBlocks[column+2][row].setToRemove(true);
				return true;
			}
		}
		return false;
	}
	
	private Block generateRandomBlock(final int column, final int row, final Image blocksResource){
		Block b = new Block(column, row, BlockType.getId(r.nextInt(BlockType.values().length)), blocksResource);
		b.setX(column * BlockType.width + position.x);
		b.setY(row * BlockType.height + position.y);
		return b;
	}

	public Block retrievedBlockFromClick(int mouseX, int mouseY) {
		if(mouseX>=position.x && mouseX<=position.x+blocks.length * BlockType.width && 
		   mouseY>=position.y && mouseY<=position.y+blocks[0].length * BlockType.height) {
			
			int column = new Double(Math.floor(( (float)mouseX - position.x ) /  BlockType.width)).intValue();
			int row    = new Double(Math.floor(( (float)mouseY - position.y ) /  BlockType.height)).intValue();
			System.out.println("["+((float)mouseX - position.x)+"/ "+ BlockType.width+"]");
			System.out.println("["+((float)mouseY - position.y)+"/ "+ BlockType.height+"]");
			System.out.println("["+column+", "+row+"]");
			
			return blocks[column][row];
		} else 
			return null;
	}
	
	public boolean isContiguousBlock(Block b1, Block b2){
		return (b1.getColumn()>=b2.getColumn()-1) && (b1.getColumn()<=b2.getColumn()+1) 
		&& (b1.getRow()>=b2.getRow()-1) && (b1.getRow()<=b2.getRow()+1); 
	}
	
	public PosInBlocks getBlockPositionInArray(final Block b){
		int column = new Double(Math.floor(( b.getX() - position.x ) /  BlockType.width)).intValue();
		int row    = new Double(Math.floor(( b.getY() - position.y ) /  BlockType.height)).intValue();
		return new PosInBlocks(column, row);
	}
	
	
	

}
